package main

import (
	"encoding/json"
	"fmt"
	"net"
	"strings"
)

// 保存用户信息的结构体
type Client struct {
	Chan chan []byte //传递用户数据
	Addr string      //客户端的IP
}

// 消息类型
type Msg struct {
	Content string // 消息内容
	User    string // 消息发布者
}

var onlineClients = make(map[string]Client) //保存所有用户  {"Addr":{"Addr":Addr,"Chan":Chan}}
var broadcast = make(chan Msg)              // 广播管道

// 监听broadcast通道中的数据,一旦有数据，循环写入每一个Client的Chan中,进而是每一个客户端收到该广播数据
func MessageManager() {
	for {
		msg := <-broadcast //读取message通道中的数据，如果通道中没有数据，就会一直等待。
		for _, client := range onlineClients {
			msgBytes, _ := json.Marshal(msg)
			client.Chan <- msgBytes
		}
	}
}

// 监听该客户端的管道，一旦有广播数据,写入socket管道，发给客户端
func WriteMsgToClient(conn net.Conn, client Client) {
	for {
		msgBytes := <-client.Chan   //读取C通道中的数据，如果没有数据，就会一直等待
		_, _ = conn.Write(msgBytes) //把json字节串数据输出到客户端
	}
}

// 监听该客户端的socket管道，一旦有数据,写入broadcast管道，进而发给每一个客户端
func read(conn net.Conn) {
	for true {
		data := make([]byte, 1024)
		n, _ := conn.Read(data)
		content := strings.TrimSpace(string(data[:n]))
		broadcast <- Msg{Content: content, User: conn.RemoteAddr().String()}
		fmt.Printf("%s发来消息:%s\n", conn.RemoteAddr().String(), content)
	}
}

// 为每一个客户端开启协程处理函数
func HandleConnect(conn net.Conn) {
	//把客户端的用户信息保存在map对象
	addr := conn.RemoteAddr().String() //获取客户端的IP
	fmt.Printf("来自客户端【%s】的连接\n", addr)
	//把用户信息封装成Client
	client := Client{make(chan []byte), addr}
	onlineClients[addr] = client
	//向所有用户广播消息
	content := client.Addr + "已上线!"
	broadcast <- Msg{Content: content, User: "系统消息"}
	//启动WriteMsgToClient的Go程
	go read(conn)
	go WriteMsgToClient(conn, client)
}

// 主协程
func main() {
	fmt.Println("聊天室服务端启动了...")
	//创建一个监听器
	listener, err := net.Listen("tcp", "0.0.0.0:8080")
	if err != nil {
		fmt.Println("net.Listen err: ", err)
		return //结束主协程
	}

	//负责监听广播通道中的数据
	go MessageManager()

	for {
		conn, err := listener.Accept() //阻塞方法，监听客户端的连接
		if err != nil {
			fmt.Println("listener.Accept err: ", err)
			continue //结束当次循环
		}
		go HandleConnect(conn)
	}

}
